package objetos;

import interfaces.Atividade;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Stack;
import java.util.TreeMap;

/**
 * Mantem o historico de atividades de todos os usuarios. Implementa o padrao Singleton.
 * 
 * @author Felipe Jose, Filipe Alencar, Rodrigo de Brito, Thiago Goncalves, Victor Hugo Arcelino;
 *
 */
public class HistoricoDeAtividades {

	private Map<Usuario,Stack<Atividade>> mapaDeAtividades;
	//private static Stack<Atividade> todasAtividades;
	private static Map<Integer,Atividade> todasAtividades;
	private static HistoricoDeAtividades _historico;
	private final int INIDICE_INICIAL = 0;
	private static int tempo;
	private static int identificadorFinal = 0;


	private HistoricoDeAtividades(){
		mapaDeAtividades = new HashMap<Usuario, Stack<Atividade>>();
		//todasAtividades = new Stack<Atividade>();
		todasAtividades = new TreeMap<Integer,Atividade>();
	}

	/**
	 * Retorna uma unica instancia de historico.
	 * @return Uma instancia da classe HistoricoDeAtividades.
	 */
	public static HistoricoDeAtividades getInstance(){
		return(_historico == null)? _historico = new HistoricoDeAtividades(): _historico;
	}

	/**
	 * Gera um novo id para uma ativiadade
	 * @return O id da atividade
	 */
	private String gerarId(){
		Random rand = new Random();
		int id = 100000+rand.nextInt(899999);
		return ""+ id + identificadorFinal;
	
	}
    
	/**
	 * Adiciona uma nova atividade a lista de atividades do usuario e ao mapa que contem todas as atividades dos usuarios
	 * @param usuario O usuario que realiza a atividade.
	 * @param atividade A atividade realizada pelo usuario.
	 * @throws Exception Se o usuario for null.
	 */
	public void addAtividades(Usuario usuario, Atividade atividade) throws Exception{
		if(usuario == null){
			throw new Exception("Usuario nao pode ser null");
		}
		if (atividade.getIdAtividade()==null){
			String idAtiv = gerarId();		
			atividade.setIdAtividade(idAtiv);
		}
		if(mapaDeAtividades.containsKey(usuario) && !mapaDeAtividades.get(usuario).contains(atividade) ){
			mapaDeAtividades.get(usuario).push(atividade);	
			todasAtividades.put(tempo++,atividade);

		}else if(!mapaDeAtividades.containsKey(usuario)){
			Stack<Atividade> pilhaDeAtividades = new Stack<Atividade>();
			pilhaDeAtividades.push(atividade);
			mapaDeAtividades.put(usuario,pilhaDeAtividades);
			todasAtividades.put(tempo++,atividade);

		}

	}
	
	/**
	 * Exibe todas as atividades realizadas pelo usuario.
	 * @param usuario O usuario que terá suas atividades exibidas.
	 * @return Uma string representando todas as atividades do usuario.
	 */
	public String imprimeAtividadesDoUsuario(Usuario usuario) {
		String saida = "";
		if(mapaDeAtividades.containsKey(usuario)){
			Stack<Atividade> atividadesDoUsuario = mapaDeAtividades.get(usuario);
			int posicaoProximo = atividadesDoUsuario.size() - 1; // o ultimo elemento do vetor corresponde ao primeiro elemento da pilha.
			for (int i = 0; i < atividadesDoUsuario.size(); i++) {
				Atividade proximaAtividade = atividadesDoUsuario.get(posicaoProximo);
				if(!(proximaAtividade instanceof AtividadeRegistroDeInteresseEmItem && 
						((AtividadeRegistroDeInteresseEmItem)proximaAtividade).getNomeDoDono().equals(usuario.getNome())))
					saida += proximaAtividade.getTemplate() + "; ";
				posicaoProximo--;
			}	
			saida = saida.substring(INIDICE_INICIAL, saida.length() - 2);
		}else{
			return "Não há atividades";
		}

		return saida;
	}

	/**
	 * Imprime um historico de atividades conjunto do usuario e de seus amigos.
	 * @param usuario O usuario que requisitou a impressao do historico de atividades conjunto.
	 * @return Uma string representando todo o historico de atividades conjunto.
	 * @throws Exception
	 */
	public String imprimeAtividadesConjuntas(Usuario usuario) throws Exception{
		String atividadesConjuntas = "";
		if(!mapaDeAtividades.containsKey(usuario)){
			return "Não há atividades";
		}else{
			List<Atividade> todasAtividades = new ArrayList<Atividade>(getTodasAtividades().values());
			Collections.reverse(todasAtividades); // coloca as atividades na ordem das mais recentes para as mais antigas.
			List<Atividade> atividadesPassadas = new ArrayList<Atividade>();
			List<Usuario> listaDeAmigos = usuario.getListaDeAmigosAdicionados();
			Stack<Atividade> atividadesDoUsuario = mapaDeAtividades.get(usuario);
			for (Usuario amigo : listaDeAmigos) {
				Stack<Atividade>atividadesDoAmigo = mapaDeAtividades.get(amigo);
				for (Iterator<Atividade> iterator = todasAtividades.iterator(); iterator
				.hasNext();) {
					Atividade proximaAtividade = iterator.next();
					if(proximaAtividade instanceof AtividadeDeAdicaoDeAmigo){
						/* Se amigo e usuario tiverem a mesma atividade do tipo AtividadeDeAdicaoDeAmigo,
					é exibida a atividade do usuario que requisitou a exibicao do historico conjunto*/
						if(atividadesDoUsuario.contains(proximaAtividade) && atividadesDoAmigo.contains(proximaAtividade)
								&& !atividadesPassadas.contains(proximaAtividade)){
							int posicao = atividadesDoUsuario.indexOf(proximaAtividade);
							proximaAtividade = atividadesDoUsuario.get(posicao);
							atividadesConjuntas += proximaAtividade.getTemplate() + "; ";
							atividadesPassadas.add(proximaAtividade);
						}else if(atividadesDoAmigo.contains(proximaAtividade) && !(atividadesDoUsuario.contains(proximaAtividade))
								&& !atividadesPassadas.contains(proximaAtividade)){
							atividadesConjuntas += proximaAtividade.getTemplate() + "; ";
							atividadesPassadas.add(proximaAtividade);
						}else if(atividadesDoUsuario.contains(proximaAtividade) && !(atividadesDoAmigo.contains(proximaAtividade))
								&& !atividadesPassadas.contains(proximaAtividade)){
							atividadesConjuntas += proximaAtividade.getTemplate() + "; ";
							atividadesPassadas.add(proximaAtividade);
						}
					}else{
						if(atividadesDoAmigo.contains(proximaAtividade) && !(atividadesDoUsuario.contains(proximaAtividade) )
								&& !atividadesPassadas.contains(proximaAtividade)){
							if(proximaAtividade instanceof AtividadeRegistroDeInteresseEmItem){
								if(((AtividadeRegistroDeInteresseEmItem)proximaAtividade).getNomeDoUsuarioInteressado().equals(usuario) ||
										((AtividadeRegistroDeInteresseEmItem)proximaAtividade).getNomeDoUsuarioInteressado().equals(amigo.getNome())){
									atividadesConjuntas += proximaAtividade.getTemplate() + "; ";
									atividadesPassadas.add(proximaAtividade);
								}
							
							}else{
								atividadesConjuntas += proximaAtividade.getTemplate() + "; ";
								atividadesPassadas.add(proximaAtividade);
							}

						}
						else if(atividadesDoUsuario.contains(proximaAtividade) && !atividadesDoAmigo.contains(proximaAtividade)
								&& !atividadesPassadas.contains(proximaAtividade)){
							
							atividadesConjuntas += proximaAtividade.getTemplate() + "; ";
							atividadesPassadas.add(proximaAtividade);

						}

						else if(atividadesDoUsuario.contains(proximaAtividade) && atividadesDoAmigo.contains(proximaAtividade)
								&& !atividadesPassadas.contains(proximaAtividade)){

							atividadesConjuntas += proximaAtividade.getTemplate() + "; ";
							atividadesPassadas.add(proximaAtividade);

						}
						else if(proximaAtividade instanceof AtividadeDePedirItem){
							for(Usuario amigo2 : listaDeAmigos){
								Stack<Atividade>atividadesDoAmigo2 = mapaDeAtividades.get(amigo2);
								if(atividadesDoAmigo2.contains(proximaAtividade) && 
								   !atividadesPassadas.contains(proximaAtividade)){
										atividadesConjuntas += proximaAtividade.getTemplate() + "; ";
										atividadesPassadas.add(proximaAtividade);
								}
							}
						}

					}
				}
			}
		}

		if(!atividadesConjuntas.trim().isEmpty())
			atividadesConjuntas = atividadesConjuntas.substring(INIDICE_INICIAL, atividadesConjuntas.length() - 2);
		return atividadesConjuntas;

	}

	/**
	 * Retorna um mapa das atividades realzidas por cada usuario do sistema.
	 * @return O mapa das atividades de cada usuario.
	 */
	public Map<Usuario, Stack<Atividade>> getMapaDeAtividades() {
		return mapaDeAtividades;
	}

	/**
	 *  Retorna um mapa que contem como chave um inteiro, que representa o quao recentemente a atividade foi realizada,
	 *  e como valor a respectiva atividade.
	 * @return O mapa de todas as atividades.
	 */
	public static Map<Integer, Atividade> getTodasAtividades() {
		return todasAtividades;
	}
}
